#pragma once

#include "ConcurrentQueue.h"

struct RUdpPacket;
class ClientList;

class RUdpSocket
{
public:
  enum Status
  {
    Disconnected,
    Connecting,
    Connected
  };

public:
  RUdpSocket();
  RUdpSocket(const RUdpSocket& socket, const sockaddr_in& client, const sockaddr_in& server);
  ~RUdpSocket();

  UINT64 HashEndPoint();

  int Connect(char* address, short port);

  SOCKET GetSocket() const { return mSocket; }
  bool Bind(char* address, short port);

  size_t Receive(char* buffer, int len);
  size_t Receive(std::vector<char>& buffer);
  int SendTo(const char* data, size_t len);

  int Close() { return closesocket(mSocket); }
  const sockaddr_in& GetLocalEndpoint() const { return mLocalEndpoint; }
  const sockaddr_in& GetRemoteEndpoint() const { return mRemoteEndpoint; }
  
  void SetSessionList(ClientList* list) { mSessionList = list; }
  ClientList* GetSessionList() { return mSessionList; }

  void CreateCleanupTimer();
  void KillCleanupTimer();

  DWORD Age() const { return timeGetTime() - mTimeStamp; }

  void OnPacketReceive();

  void Push(RUdpPacket* packet);

  // Crude version of a smart pointer. It's all we needfor this example.
  long AddRef();
  void Release();

  void SetTimeStamp(DWORD time) { mTimeStamp = timeGetTime(); }
  UINT64 GetHash() const { return mHash; }

  void SetCleanupTimer(PTP_TIMER timer) { mCleanupTimer = timer; }

  Status GetConnectionStatus() { return mStatus; }

private:
  static void CALLBACK CleanupSession(PTP_CALLBACK_INSTANCE instance, PVOID param, PTP_TIMER timer);
  SOCKET CreateSocket();
  int SendTo(const RUdpPacket& packet);

  void ProcessMessagePacket(RUdpPacket* packet, bool outOfOrder);
  void ProcessReceivedPacket(RUdpPacket* packet, bool outOfOrder=false);
  void ProcessOutOfOrderPackets();

private:
  // TODO: Replace with a ringbuffer or vector (optimization)
  typedef std::vector<RUdpPacket*> OrderedSegments;

  ConcurrentQueue<RUdpPacket*> mPackets;
  OrderedSegments mOrderedPackets;
  DWORD mTimeStamp;
  volatile long mCount;
  volatile long mProcessing;
  ClientList* mSessionList;
  UINT64 mHash;
  PTP_TIMER mCleanupTimer;

  int mLastSentSeqId;
  int mLastRcvdSeqId;
  int mBacklogCount;

  SOCKET mSocket;
  sockaddr_in mLocalEndpoint;
  sockaddr_in mRemoteEndpoint;

  Status mStatus;
};
